package rest.jbpm.identity;

import com.novell.ldap.*;
import java.util.*;
import org.jbpm.api.identity.*;
import org.jbpm.api.identity.User;
import org.jbpm.pvm.internal.identity.spi.IdentitySession;
import org.slf4j.*;
import rest.jbpm.services.DNC14N;

public class LdapIdentitySession implements IdentitySession {
	
	private static Logger log = LoggerFactory.getLogger(LdapIdentitySession.class);
	private static final String GRP_NAME = "dc=GRP,dc=eu,dc=bmstu,dc=ru";
	private static final String USR_NAME = "dc=USR,dc=eu,dc=bmstu,dc=ru";
    private static final String EU_NAME = "dc=eu,dc=bmstu,dc=ru";
    private final LDAPConnection connection = new LDAPConnection();
    private String host = "ldap.eu.bmstu.ru";
    private int port =  8389;
    	
	public LdapIdentitySession() {
        init();

	}
	
	private void init() {
        try {
            connection.connect(host, port);
            connection.bind(LDAPConnection.LDAP_V3, "Anonymous", "".getBytes());
        } catch (LDAPException ex) {
            log.error("error occured while LDAP initialization",ex);
        }
	}

    @Override
    protected void finalize (){
        try {
            connection.disconnect();
        } catch (LDAPException ex) {
           log.error("destructor", ex);
        }
    }

	@Override
	public String createGroup(String arg0, String arg1, String arg2) {
		throw new UnsupportedOperationException("not supported. Use LDAP manager instead");
	}

	@Override
	public void createMembership(String arg0, String arg1, String arg2) {
		throw new UnsupportedOperationException("not supported. Use LDAP manager instead");		
	}

	@Override
	public String createUser(String arg0, String arg1, String arg2, String arg3) {
		throw new UnsupportedOperationException("not supported. Use LDAP manager instead");
	}

	@Override
	public void deleteGroup(String arg0) {
		throw new UnsupportedOperationException("not supported. Use LDAP manager instead");
	}

	@Override
	public void deleteMembership(String arg0, String arg1, String arg2) {
		throw new UnsupportedOperationException("not supported. Use LDAP manager instead");
	}

	@Override
	public void deleteUser(String arg0) {
		throw new UnsupportedOperationException("not supported. Use LDAP manager instead");
	}

	@Override
	public Group findGroupById(String id) {

       id = DNC14N.c14n(id);
       try {
            LDAPSearchConstraints cons = new LDAPSearchConstraints();
            cons.setTimeLimit(10000);
            LDAPSearchResults searchResults = connection.search(GRP_NAME,
                LDAPConnection.SCOPE_SUB,String.format("(entryDN:dnSubtreeMatch:=%s)",id),
                new String[]{"dn","displayName","bmstuGroupType","uniqueIdentifier"},false, cons);
            
            while (searchResults.hasMore()){
                LDAPEntry nextEntry = searchResults.next();
                LDAPAttributeSet attributes = nextEntry.getAttributeSet();
                return new ExtendedGroup(
                        nextEntry.getDN(),
                        attributes.getAttribute("displayName") == null ?
                            "none" : attributes.getAttribute("displayName").getStringValue(),
                        attributes.getAttribute("bmstuGroupType") == null ?
                            "none" : attributes.getAttribute("bmstuGroupType").getStringValue(),
                        attributes.getAttribute("uniqueIdentifier") == null ?
                            "none" : attributes.getAttribute("uniqueIdentifier").getStringValue());
            }
        } catch (LDAPException ex) {
            log.error("group not found", ex);

        }
        return null;

	}

	@Override
	public List<Group> findGroupsByUser(String id) {

        id = DNC14N.c14n(id);
        List<Group> groups = new ArrayList<Group>();
        try {
            LDAPSearchConstraints cons = new LDAPSearchConstraints();
            cons.setTimeLimit(10000);
            LDAPSearchResults searchResults = connection.search(GRP_NAME,
                LDAPConnection.SCOPE_SUB,
                String.format("(|(&(objectClass=bmstuGroup)(member=%s))(objectClass=bmstuCGroup))",id),
                new String[]{"member","displayName","bmstuGroupType","uniqueIdentifier"}, false, cons);

            while (searchResults.hasMore()){
                LDAPEntry nextEntry = searchResults.next();
                LDAPAttributeSet attributes = nextEntry.getAttributeSet();
                if(attributes.getAttribute("member") == null) continue;
                for(String member : attributes.getAttribute("member").getStringValueArray()) {
                    if(member.equalsIgnoreCase(id)) {
                        groups.add(
                                new ExtendedGroup(nextEntry.getDN(),
                                    attributes.getAttribute("displayName") == null ?
                                        "none" : attributes.getAttribute("displayName").getStringValue(),
                                    attributes.getAttribute("bmstuGroupType") == null ?
                                        "none" : attributes.getAttribute("bmstuGroupType").getStringValue(),
                                    attributes.getAttribute("uniqueIdentifier") == null ?
                                        "none" : attributes.getAttribute("uniqueIdentifier").getStringValue()));
                        break;
                    }
                }
            }
        } catch (LDAPException ex) {
            log.error("group not found", ex);
        }
        return groups;
	}

	@Override
	public List<Group> findGroupsByUserAndGroupType(String id, String groupType) {

        id = DNC14N.c14n(id);
        List<Group> groups = new ArrayList<Group>();
        try {
            LDAPSearchConstraints cons = new LDAPSearchConstraints();
            cons.setTimeLimit(10000);
            LDAPSearchResults searchResults = connection.search(GRP_NAME,
                LDAPConnection.SCOPE_SUB,
                String.format("(|(&(objectClass=bmstuGroup)(member=%s))(objectClass=bmstuCGroup))",id),
                new String[]{"member","displayName","bmstuGroupType","uniqueIdentifier"}, false, cons);

            while (searchResults.hasMore()){

                LDAPEntry nextEntry = searchResults.next();
                LDAPAttributeSet attributes = nextEntry.getAttributeSet();
                if((attributes.getAttribute("member") == null)
                        || (attributes.getAttribute("bmstuGroupType") == null)
                        || (!groupType.equalsIgnoreCase(attributes.getAttribute("bmstuGroupType").getStringValue()))) continue;
                for(String member : attributes.getAttribute("member").getStringValueArray()) {
                    if(member.equalsIgnoreCase(id)) {
                        groups.add(
                                new ExtendedGroup(
                                    nextEntry.getDN(),
                                    attributes.getAttribute("displayName") == null ?
                                        "none" : attributes.getAttribute("displayName").getStringValue(),
                                    attributes.getAttribute("bmstuGroupType") == null ?
                                        "none" : attributes.getAttribute("bmstuGroupType").getStringValue(),
                                    attributes.getAttribute("uniqueIdentifier") == null ?
                                        "none" : attributes.getAttribute("uniqueIdentifier").getStringValue()));
                        break;
                    }
                }
            }
        } catch (LDAPException ex) {
            log.error("group not found", ex);
        }
        return groups;
	}

	@Override
	public User findUserById(String id) {

       try {
            id = DNC14N.c14n(id);
            LDAPSearchConstraints cons = new LDAPSearchConstraints();
            cons.setTimeLimit(10000);
            LDAPSearchResults searchResults = connection.search(USR_NAME,
                LDAPConnection.SCOPE_SUB,String.format("(entryDN:dnSubtreeMatch:=%s)",id),
                new String[]{"mail","sn","cn","telephoneNumber"},false, cons);

            while (searchResults.hasMore()){

                final LDAPEntry nextEntry = searchResults.next();
                final LDAPAttributeSet attributes = nextEntry.getAttributeSet();

                return new ExtendedUser(nextEntry.getDN(),
                        attributes.getAttribute("sn") == null ?
                            "none" : attributes.getAttribute("sn").getStringValue(),
                        attributes.getAttribute("cn") == null ?
                            "none" : attributes.getAttribute("cn").getStringValue(),
                        attributes.getAttribute("mail") == null ?
                            "none" : attributes.getAttribute("mail").getStringValue(),
                        attributes.getAttribute("telephoneNumber") == null ?
                            "telephoneNumber" : attributes.getAttribute("telephoneNumber").getStringValue());
            }
        } catch (LDAPException ex) {
            log.error("user not found", ex);
        }
        return null;
	}

	@Override
	public List<User> findUsers() {

        List<User> users = new ArrayList<User>();
        
        try {
            
            LDAPSearchConstraints cons = new LDAPSearchConstraints();
            cons.setTimeLimit(10000);
            LDAPSearchResults searchResults = connection.search(USR_NAME,
                LDAPConnection.SCOPE_SUB,"objectClass=bmstuPerson",
               new String[] {"mail","sn","cn","telephoneNumber"},false, cons);

            while (searchResults.hasMore()){

                final LDAPEntry nextEntry = searchResults.next();
                final LDAPAttributeSet attributes = nextEntry.getAttributeSet();

                users.add( new ExtendedUser(nextEntry.getDN(),
                        attributes.getAttribute("sn") == null ?
                            "none" : attributes.getAttribute("sn").getStringValue(),
                        attributes.getAttribute("cn") == null ?
                            "none" : attributes.getAttribute("cn").getStringValue(),
                        attributes.getAttribute("mail") == null ?
                            "none" : attributes.getAttribute("mail").getStringValue(),
                        attributes.getAttribute("telephoneNumber") == null ?
                            "telephoneNumber" : attributes.getAttribute("telephoneNumber").getStringValue()));

            }
        } catch (LDAPException ex) {
            log.error("user not found", ex);
        }
        return users;
	}

	@Override
	public List<User> findUsersByGroup(String groupId) {

        try {

            HashMap<String,User> users = new HashMap<String, User>();
            groupId = DNC14N.c14n(groupId);
            LDAPSearchConstraints cons = new LDAPSearchConstraints();
            cons.setTimeLimit(10000);
            LDAPSearchResults searchResults =
                connection.search(GRP_NAME,LDAPConnection.SCOPE_SUB,
                    String.format("(&(objectclass=bmstuCGroup)(entryDN:dnSubtreeMatch:=%s))",groupId),
                    new String[]{"member"}, false, cons);

            while (searchResults.hasMore()){

                LDAPEntry nextEntry = searchResults.next();
                if(nextEntry.getAttribute("member") != null) {
                    for(String mmbr : nextEntry.getAttribute("member").getStringValueArray()) {
                        if(!users.containsKey(mmbr)) {
                            User u = findUserById(mmbr);
                            if(u != null) users.put(mmbr,u);
                        }
                    }
                }
            }
            
            return new ArrayList<User>(users.values());

        } catch (LDAPException ex) {
            log.error("user not found", ex);
            return null;
        }

	}


	@Override
	public List<User> findUsersById(String ... ids) {

        List<User> users = new ArrayList<User>();
        for(String id : ids)
            users.add(findUserById(DNC14N.c14n(id)));
		return users;
	}

}
